package com.lzy.crm.wechat.util;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.math.BigInteger;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.net.ssl.HttpsURLConnection;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import com.alibaba.fastjson.JSONObject;
import com.google.gson.JsonObject;
import com.lzy.crm.base.util.JsonUtil;
import com.lzy.crm.wechat.consts.WechatPayProperties;
import com.lzy.crm.wechat.vo.AccessToken;
import com.lzy.crm.wechat.vo.Article;
import com.lzy.crm.wechat.vo.Message;
import com.lzy.crm.wechat.vo.Reply;
import com.lzy.crm.wechat.vo.WxPayDto;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.core.util.QuickWriter;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import com.thoughtworks.xstream.io.xml.XppDriver;







/**
 * @author csp
 * @date 2015.6.09
 * 请求数据通用类*/
public class WeixinUtil {
   
	/**
	 * logger
	 */
	public static final Logger logger = LogManager.getLogger(WeixinUtil.class);
	
	public static final String TOKEN = "cspTest";
	
	// 获取access_token的接口地址（GET）   
	public final static String access_token_url=WechatPayProperties.getAccessTokenUrl() ;//"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=AppID&secret=Secret";
	
	// 获取 ticket 接口地址
	public final static String jsapi_ticket_url=WechatPayProperties.getJsapiTicketUrl() ;//"https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi";
	
	//微信创建菜单接口
	private final static String creatMenuUrl = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN";
	//创建个性化菜单
	private final static String creatSpecialMenu = "https://api.weixin.qq.com/cgi-bin/menu/addconditional?access_token=ACCESS_TOKEN";
	

	
	/** 
	 * 获取access_token 
	 *  
	 * @param CorpID 企业Id 
	 * @param SECRET 管理组的凭证密钥，每个secret代表了对应用、通讯录、接口的不同权限；不同的管理组拥有不同的secret 
	 * @return 
	 */  
	public static AccessToken getAccessToken(String appID, String secret) {  
	    AccessToken accessToken = null;  
	    
	    
	    String requestUrl = access_token_url.replace("AppID", appID).replace("Secret", secret);  
	    JsonObject jsonObject = HttpRequest(requestUrl, "GET", null);  
	    // 如果请求成功  
	    if (null != jsonObject) {  
	        try {  
	            accessToken = new AccessToken();  
	            accessToken.setToken(jsonObject.get("access_token").getAsString() );  
	            accessToken.setExpiresIn(jsonObject.get("expires_in").getAsInt());
	        } catch (Exception e) {  
	            String error = String.format("获取token失败 errcode:{} errmsg:{}", jsonObject.get("errcode"), jsonObject.get("errmsg"));  
	            logger.error(error);
	        }  
	    }  
	    return accessToken;  
	}
	
	
	/**
	 * 根据token计算signature验证是否为微信服务端发送的消息
	 * @param request
	 * @return
	 */
	public static boolean checkWeixinReques(HttpServletRequest request){
		String signature = request.getParameter("signature");
		String timestamp = request.getParameter("timestamp");
		String nonce = request.getParameter("nonce");
		if (signature != null && timestamp != null && nonce != null ) {
			String[] strSet = new String[] { TOKEN, timestamp, nonce };
			java.util.Arrays.sort(strSet);
			String key = "";
			for (String string : strSet) {
				key = key + string;
			}
			String pwd = WeixinUtil.sha1(key);
			return pwd.equals(signature);
		}else {
			return false;
		}
	}
	
	/**
	 * sha1加密算法
	 * @param key需要加密的字符串
	 * @return 加密后的结果
	 */
	public static String sha1(String key) {
		try {
			MessageDigest md = MessageDigest.getInstance("SHA1");
			md.update(key.getBytes());
			String pwd = new BigInteger(1, md.digest()).toString(16);
			return pwd;
		} catch (Exception e) {
			e.printStackTrace();
			return key;
		}
	}
	
	/**
	 * 解析request中的xml 并将数据存储到一个Map中返回
	 * @param request
	 */
	public static Map<String, String> parseXml(HttpServletRequest request){
		Map<String, String> map = new HashMap<String, String>();
		try {
			InputStream inputStream = request.getInputStream();
			SAXReader reader = new SAXReader();
			Document document = reader.read(inputStream);
			Element root = document.getRootElement();
			List<Element> elementList = root.elements();
			for (Element e : elementList)
				//遍历xml将数据写入map
				map.put(e.getName(), e.getText());
			inputStream.close();
			inputStream = null;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return map;
	}
	
	/**
	 * 存储数据的Map转换为对应的Message对象
	 * @param map 存储数据的map
	 * @return 返回对应Message对象
	 */
	public static  Message  mapToMessage(Map<String,String> map){
		if(map == null) return null;
		String msgType = map.get("MsgType");
		Message message = new Message();
		message.setToUserName(map.get("ToUserName"));
		message.setFromUserName(map.get("FromUserName"));
		if(map.get("CreateTime")!=null){
			message.setCreateTime(new Date(Long.parseLong(map.get("CreateTime"))));
		}
		message.setMsgType(msgType);
		message.setMsgId(map.get("MsgId"));
		if(msgType.equals(Message.TEXT)){
			message.setContent(map.get("Content"));
		}else if(msgType.equals(Message.IMAGE)){
			message.setPicUrl(map.get("PicUrl"));
		}else if(msgType.equals(Message.LINK)){
			message.setTitle(map.get("Title"));
			message.setDescription(map.get("Description"));
			message.setUrl(map.get("Url"));
		}else if(msgType.equals(Message.LOCATION)){
			message.setLocationX(map.get("Location_X"));
			message.setLocationY(map.get("Location_Y"));
			message.setScale(map.get("Scale"));
			message.setLabel(map.get("Label"));
		}else if(msgType.equals(Message.EVENT)){
			message.setEvent(map.get("Event"));
			message.setEventKey(map.get("EventKey"));
		}
		return message;
	}
	
	/**
	 * XML转换
	 */
	private static XStream xstream = new XStream(new XppDriver() {  
        public HierarchicalStreamWriter createWriter(Writer out) {  
            return new PrettyPrintWriter(out) {  
                // 对所有xml节点的转换都增加CDATA标记  
                boolean cdata = true;  
                @SuppressWarnings("unchecked")  
                public void startNode(String name, Class clazz) {  
                    super.startNode(name, clazz);  
                }  
                protected void writeText(QuickWriter writer, String text) {  
                    if (cdata) {  
                        writer.write("<![CDATA[");  
                        writer.write(text);  
                        writer.write("]]>");  
                    } else {  
                        writer.write(text);  
                    }  
                }  
            };  
        }  
    });
	
	/**
	 * 将回复消息对象转换成xml字符串
	 * @param reply 回复消息对象
	 * @return 返回符合微信接口的xml字符串
	 */
	public static String replyToXml(Reply reply){
		String type = reply.getMsgType();
		if(Reply.TEXT.equals(type)){
			xstream.omitField(Reply.class, "articles");
			xstream.omitField(Reply.class, "articleCount");
			xstream.omitField(Reply.class, "musicUrl");
			xstream.omitField(Reply.class, "hQMusicUrl");
		}else if(Reply.MUSIC.equals(type)){
			xstream.omitField(Reply.class, "articles");
			xstream.omitField(Reply.class, "articleCount");
			xstream.omitField(Reply.class, "content");
		}else if(Reply.NEWS.equals(type)){
			xstream.omitField(Reply.class, "content");
			xstream.omitField(Reply.class, "musicUrl");
			xstream.omitField(Reply.class, "hQMusicUrl");
		}
		xstream.autodetectAnnotations(true);  
		xstream.alias("xml", reply.getClass());
		xstream.alias("item", new Article().getClass());
		return xstream.toXML(reply);
	}
	
	
	
	/** 
	 * 获取access_token 
	 *  
	 * @param CorpID 企业Id 
	 * @param SECRET 管理组的凭证密钥，每个secret代表了对应用、通讯录、接口的不同权限；不同的管理组拥有不同的secret 
	 * @return 
	 */  
	public static String getJsapiTicket(String access_token) {  
		String ticket="";
	    String requestUrl = jsapi_ticket_url.replace("ACCESS_TOKEN", access_token);  
	    try {
	    	JsonObject jsonObject = HttpRequest(requestUrl, "GET", null);  
			if(jsonObject!=null){
				ticket=jsonObject.get("ticket").getAsString();
			}
		} catch (Exception e) {
			logger.catching(e);
		}
	    return ticket;  
	}
	
	
	
	
	@SuppressWarnings("static-access")
	public static Map<String, String>  getPackage(WxPayDto tpWxPayDto) {
		
		String openId = tpWxPayDto.getOpenId();
		// 1 参数
		
		// 附加数据 原样返回
		String attach = "";
		// 总金额以分为单位，不带小数点
		String totalFee = getMoney(tpWxPayDto.getTotalFee());
		
		// 订单生成的机器 IP
		String spbill_create_ip = tpWxPayDto.getSpbillCreateIp();
		
		// 商户订单号
		// 订单号
		String out_trade_no = tpWxPayDto.getOrderId();
		
		// 这里notify_url是 支付完成后微信发给该链接信息，可以判断会员是否支付成功，改变订单状态等。
		String notify_url = WechatPayProperties.getNotify_url() + out_trade_no + ".html" ;// notifyurl;
		//String notify_url = "http://test.wx.jlpay.com/mt_merchant_web/wechat/merchant/payResult/" + out_trade_no + ".mt" ;// notifyurl;
//		
		String trade_type = "JSAPI";

		// ---必须参数
		// 商户号
		String mch_id = WechatPayProperties.getPartnerId();
		// 随机字符串
		String nonce_str = getNonceStr();

		// 商品描述根据情况修改
		String body = tpWxPayDto.getBody();

		

		SortedMap<String, String> packageParams = new TreeMap<String, String>();
		packageParams.put("appid", WechatPayProperties.getAppId());
		packageParams.put("mch_id", mch_id);
		packageParams.put("nonce_str", nonce_str);
		packageParams.put("body", body);
		packageParams.put("attach", attach);
		packageParams.put("out_trade_no", out_trade_no);

		// 这里写的金额为1 分到时修改
		packageParams.put("total_fee", totalFee);
		packageParams.put("spbill_create_ip", spbill_create_ip);
		packageParams.put("notify_url", notify_url);

		packageParams.put("trade_type", trade_type);
		packageParams.put("openid", openId);

		String sign = Sign.createSign(packageParams); 
		
		String xml = "<xml>" + "<appid>" +  WechatPayProperties.getAppId() + "</appid>" + "<mch_id>"
				+ mch_id + "</mch_id>" + "<nonce_str>" + nonce_str
				+ "</nonce_str>" + "<sign>" + sign + "</sign>"
				+ "<body><![CDATA[" + body + "]]></body>" 
				+ "<out_trade_no>" + out_trade_no
				+ "</out_trade_no>" + "<attach>" + attach + "</attach>"
				+ "<total_fee>" + totalFee + "</total_fee>"
				+ "<spbill_create_ip>" + spbill_create_ip
				+ "</spbill_create_ip>" + "<notify_url>" + notify_url
				+ "</notify_url>" + "<trade_type>" + trade_type
				+ "</trade_type>" + "<openid>" + openId + "</openid>"
				+ "</xml>";
		String createOrderURL = WechatPayProperties.getCreateOrderUrl(); // "https://api.mch.weixin.qq.com/pay/unifiedorder";
		
		Map<String,String> returnMap=new GetWxOrderno().getPayNo(createOrderURL, xml);
		System.out.println(JsonUtil.toJson(returnMap));
		Map<String, String> finalpackage = new HashMap<String, String>();
		String timestamp =getTimeStamp(); // "1419835025";// getTimeStamp();
		String packages = "prepay_id="+returnMap.get("prepay_id");
		finalpackage.put("appId", WechatPayProperties.getAppId());  
		finalpackage.put("timeStamp", timestamp);  
		finalpackage.put("nonceStr", nonce_str);//returnMap.get("nonce_str") );//nonce_str);  
		finalpackage.put("package", packages);  
		finalpackage.put("signType", "MD5");
		
		List<String[]> finalpackagesign = new ArrayList<String[]>();
		finalpackagesign.add(new String[]{"appId", WechatPayProperties.getAppId()});  
		finalpackagesign.add(new String[]{"nonceStr", finalpackage.get("nonceStr")}); //returnMap.get("nonce_str") );//nonce_str); 				 
		finalpackagesign.add(new String[]{"package", finalpackage.get("package")});   
		finalpackagesign.add(new String[]{"signType", finalpackage.get("signType")}); 
		finalpackagesign.add(new String[]{"timeStamp", finalpackage.get("timeStamp")});  
		
		String finalsign = Sign.createSign2(finalpackagesign);
		
		finalpackage.put("paySign", finalsign);
		
		return finalpackage;
	}
	
	
	public static String getTimeStamp() {
		return String.valueOf(System.currentTimeMillis() / 1000);
	}
	
	/**
	 * 获取随机字符串
	 * @return
	 */
	public static String getNonceStr() {
		return RandomStringUtils.random(8, "123456789");
	}
	
	 /**
     * 判断是否来自微信, 5.0 之后的支持微信支付
     *
     * @param request
     * @return
     */
    public static boolean isWeiXin(HttpServletRequest request) {
        String userAgent = request.getHeader("User-Agent");
        if (StringUtils.isNotBlank(userAgent)) {
            Pattern p = Pattern.compile("MicroMessenger/(\\d+).+");
            Matcher m = p.matcher(userAgent);
            String version = null;
            if (m.find()) {
                version = m.group(1);
            }
            return (null != version && NumberUtils.toInt(version) >= 5);
        }
        return false;
    }
	
	/**
	 * 元转换成分
	 * @param money
	 * @return
	 */
	public static String getMoney(String amount) {
		if(amount==null){
			return "";
		}
		// 金额转化为分为单位
		String currency =  amount.replaceAll("\\$|\\￥|\\,", "");  //处理包含, ￥ 或者$的金额  
        int index = currency.indexOf(".");  
        int length = currency.length();  
        Long amLong = 0l;  
        if(index == -1){  
            amLong = Long.valueOf(currency+"00");  
        }else if(length - index >= 3){  
            amLong = Long.valueOf((currency.substring(0, index+3)).replace(".", ""));  
        }else if(length - index == 2){  
            amLong = Long.valueOf((currency.substring(0, index+2)).replace(".", "")+0);  
        }else{  
            amLong = Long.valueOf((currency.substring(0, index+1)).replace(".", "")+"00");  
        }  
        return amLong.toString(); 
	}
	
	 /** 
     * 发起https请求并获取结果 
     *  
     * @param requestUrl 请求地址 
     * @param requestMethod 请求方式（GET、POST） 
     * @param outputStr 提交的数据 
     * @return JSONObject(通过JSONObject.get(key)的方式获取json对象的属性值) 
     */  
	public static JsonObject HttpRequest(String request , String RequestMethod , String output ){
		
		JsonObject jsonObject = null;
		StringBuffer buffer = new StringBuffer();
		try {
			//建立连接
			URL url = new URL(request);
			HttpURLConnection connection = (HttpURLConnection) url.openConnection();
			connection.setDoOutput(true);
			connection.setDoInput(true);
			connection.setUseCaches(false);
			connection.setRequestMethod(RequestMethod);
			if(output!=null){
				OutputStream out = connection.getOutputStream();
				out.write(output.getBytes("UTF-8"));
				out.close();
			}
			//流处理
			InputStream input = connection.getInputStream();
			InputStreamReader inputReader = new InputStreamReader(input,"UTF-8");
			BufferedReader reader = new BufferedReader(inputReader);
			String line;
			while((line=reader.readLine())!=null){
				buffer.append(line);
			}
			//关闭连接、释放资源
			reader.close();
			inputReader.close();
			input.close();
			input = null;
			connection.disconnect();
			jsonObject =   JsonUtil.fromJson(buffer.toString(), JsonObject.class); //  fromObject(buffer.toString());
		} catch (Exception e) {
		}
		return jsonObject;
	} 
	
	
	
	/**
	 * 根据内容类型判断文件扩展名
	 * 
	 * @param contentType 内容类型
	 * @return
	 */
	public static String getFileEndWitsh(String contentType) {
		String fileEndWitsh = "";
		if ("image/jpeg".equals(contentType))
			fileEndWitsh = ".jpg";
		else if ("audio/mpeg".equals(contentType))
			fileEndWitsh = ".mp3";
		else if ("audio/amr".equals(contentType))
			fileEndWitsh = ".amr";
		else if ("video/mp4".equals(contentType))
			fileEndWitsh = ".mp4";
		else if ("video/mpeg4".equals(contentType))
			fileEndWitsh = ".mp4";
		return fileEndWitsh;
	}
	
	
	/**********  二维码功能  ***********/
	/** 
     * 创建临时带参数二维码 
     * 
     * @param accessToken 
     * @expireSeconds 该二维码有效时间，以秒为单位。 最大不超过2592000（即30天），此字段如果不填，则默认有效期为30秒。 
     * @param sceneId  场景Id 
     * @return 
     */  
    public static String createTemporaryORCode(String accessToken, String expireSeconds ,String sceneId) {  
        // WeChatQRCode weChatQRCode = null;  
    	String ticket = "";
        String requestUrl = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=TOKEN";  
        requestUrl = requestUrl.replace("TOKEN", accessToken);  
        //需要提交的JSON数据  
        // String outputStr = "{\"expire_seconds\": %d,\"action_name\": \"QR_SCENE\", \"action_info\": {\"scene\": {\"scene_id\"：%d}}}";  
        //String outputStr = "{\"expire_seconds\": EXPIRE_SECONDS,\"action_name\": \"QR_LIMIT_SCENE\", \"action_info\": {\"scene\": {\"scene_str\"：SCENE_ID}}}";  
        String outputStr = "{\"action_name\": \"QR_LIMIT_STR_SCENE\", \"action_info\": {\"scene\": {\"scene_str\": \""
                + sceneId + "\"}}}";;  
        outputStr = outputStr.replace("EXPIRE_SECONDS", expireSeconds);  
        //outputStr = outputStr.replace("SCENE_ID", sceneId+"");  
        //创建临时带参数二维码  
        // JSONObject jsonObject = HttpRequestUtil.httpRequestJSONObject(requestUrl, HttpRequestUtil.POST_METHOD, String.format(outputStr, expireSeconds, sceneId));  
        String jsons = WeixinUtil.sendPost(requestUrl, outputStr);
        System.out.println("带参数："+outputStr);
        if(!StringUtils.isEmpty(jsons)){  
            try {  
            	JSONObject data = JSONObject.parseObject(jsons);
            	ticket = data.getString("ticket");
            	System.out.println("创建临时带参二维码成功,ticket="+ticket+",expire_seconds="+data.get("expire_seconds"));
            	logger.info("创建临时带参二维码成功,ticket="+ticket+",expire_seconds="+data.get("expire_seconds"));  
            } catch (Exception e) {
                String errorMsg = "";	// jsonObject.getString("errmsg");  
                logger.info("创建临时带参二维码失败,错误码是="+errorMsg);  
            }  
        }     
        return ticket;  
    } 
	
    
    public String createPermanentORCode(String accessToken, String sceneStr) {  
        String ticket = null;  
        String requestUrl = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=TOKEN";  
        requestUrl = requestUrl.replace("TOKEN", accessToken);  
        String outputStr = "{\"action_name\": \"QR_LIMIT_STR_SCENE\", \"action_info\":{\"scene\": {\"scene_str\":%s}}}";  
        // JSONObject jsonObject = HttpRequestUtil.httpRequestJSONObject(requestUrl, HttpRequestUtil.POST_METHOD, String.format(outputStr, sceneStr));  
        String ticketData = this.sendPost(requestUrl, String.format(outputStr, sceneStr));  
        logger.info("-----二维码参数-永久--------,ticket="+ticketData);  
        JSONObject jsonObject = new JSONObject();
        if(null!=jsonObject){             
            try {  
                ticket = jsonObject.getString("ticket");  
                logger.info("创建永久带参二维码成功,ticket="+ticket);  
            } catch (Exception e) {  
                String errorCode = jsonObject.getString("errcode");  
                logger.info("创建永久带参二维码失败,错误码是="+errorCode);  
                String errorMsg = jsonObject.getString("errmsg");  
                logger.info("创建永久带参二维码失败,错误信息是="+errorMsg);  
            }  
        }     
        return ticket;  
    }  
    
    /**
	 * 发送http post 请求
	 * @param urlString
	 * @param postJsonString
	 * @return
	 */
	public static String sendPost(String urlString, String postJsonString){
		String responseStr = "";
		if(postJsonString == null ||postJsonString.length() == 0 ){
			return responseStr;
		}
		try {
			// 创建一个url
			URL reqURL = new URL(urlString);
			// 拿取链接
			HttpsURLConnection httpsConn = (HttpsURLConnection) reqURL.openConnection();
			httpsConn.setDoOutput(true);
			// 取得该连接的输出流，以读取响应内容
			OutputStreamWriter osr = new OutputStreamWriter(httpsConn.getOutputStream());
			// 往输出流里面写入数据
			osr.write(postJsonString);
			osr.close(); //这里关闭的仅仅是输出流，连接并未关闭
			
			// 返回结果
			InputStreamReader isr = new InputStreamReader(httpsConn.getInputStream());
			// 读取服务器的响应内容并显示
			char[] chars = new char[1024];
			int len;
			while ((len = isr.read(chars)) != -1) {
				responseStr += new String(chars, 0, len);
			}
			System.out.println("返回结果:" + responseStr);
			isr.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return responseStr;
	}
	
	
	/**
	 * 创建菜单 
	 * @param menuJsonData
	 * @param menuType
	 * @return
	 */
	public static boolean createMenu(String menuJsonData, int menuType){
		AccessToken accessToken= getAccessToken(WechatPayProperties.getAppId(), WechatPayProperties.getAppSecret());
		String requestUrl = "";
		if(menuType == 1){
			requestUrl = creatMenuUrl;
		}else{
			requestUrl = creatSpecialMenu;
		}
		requestUrl = requestUrl.replace("ACCESS_TOKEN", accessToken.getToken());
		JsonObject wxResponse = HttpRequest(requestUrl, "POST", menuJsonData);
		System.out.println(JsonUtil.toJson(wxResponse));
		if(wxResponse!=null){
			if(wxResponse.get("errcode").getAsInt() == 0 ){
				return true;
			}else{
				logger.error(wxResponse.get("errcode").toString());
				return false;
			}
		}
		return false;
	}
   
	
	
	
	
	
	
	
	
	
	
}  


/*
 * 处理微信返回的错误码
 */
class WxErrcode {
	private long errcode;
	private String errmsg;
	
	@Override
	public String toString() {
		return "wechat error [errcode=" + errcode + ", errmsg=" + errmsg + "]";
	}
	
	public void setErrcode(long code){
		this.errcode = code;
	}
	
	public void setErrmsg(String errstr){
		this.errmsg = errstr;
	}
	
	public long getErrcode(){
		return errcode;
	}
	
	public String getErrmsg(){
		return errmsg;
	}
}